#ifndef http_server_h__
#define http_server_h__

#include "common.h"
#include "http_connection.h"
#include "router.h"

#include <thread>
#include <memory>
#include <future>

class http_server
{
public:
	http_server(const char* ip, unsigned short port):
	_acceptor(_io_service, boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), port)),
	_signal(_io_service, SIGINT, SIGTERM),
	_current_thread_index(0)
	{
		 _root_router = new root_router;
	}

	http_server& multi_thread(unsigned char n)
	{
		_multi_thread_num = n;
		for (uint8_t i = 0; i < _multi_thread_num; i++)
			_connection_io_service_pool.emplace_back(new boost::asio::io_service);
		LOGI("%d threads has been created for http connection", _connection_io_service_pool.size());
		return *this;
	}

	void run()
	{
		std::vector<std::future<void>> v;
		for (uint8_t i = 0; i < _multi_thread_num; i++)
		{
			v.push_back(std::async(std::launch::async, [this, i]()
			{
				boost::asio::deadline_timer timer(*_connection_io_service_pool[i]);
				timer.expires_from_now(boost::posix_time::seconds(1));

				std::function<void(const boost::system::error_code& ec)> cb;
				cb = [&](const boost::system::error_code& err)
				{
					if (err)
						return;
					timer.expires_from_now(boost::posix_time::seconds(1));
					timer.async_wait(cb);
				};
				timer.async_wait(cb);

				_connection_io_service_pool[i]->run();
			}));
		}

		_signal.async_wait([&](const boost::system::error_code& error, int signal_number)
		{
			_io_service.stop();
			for (uint8_t i = 0; i < _multi_thread_num; i++)
				_connection_io_service_pool[i]->stop();
		});

		do_accpet();

		std::thread([this]()
		{
			_io_service.run();
		}).join();
	}

	root_router* register_router(router_family* router)
	{
		if (nullptr == router)
			return _root_router;
		router->_http_server = this;
		_root_router->register_router(router);
		return _root_router;
	}

	bool delete_router(const char* name)
	{
		return _root_router->delete_router(name);
	}
private:
	void do_accpet()
	{
		auto& is = get_io_serivice();
		auto conn = http_connection::make_http_connection(is, *_root_router);

		_acceptor.async_accept(conn->_socket, [this, conn](boost::system::error_code err)
		{
			do_accpet();
			if (!err)
			{
				conn->start();
			}
		});
	}

	boost::asio::io_service& get_io_serivice()
	{
		if (++_current_thread_index >= _connection_io_service_pool.size())
			_current_thread_index = 0;
		return *_connection_io_service_pool[_current_thread_index];
	}
private:
	boost::asio::io_service _io_service;
	boost::asio::ip::tcp::acceptor _acceptor;
	unsigned short _port;
	boost::asio::signal_set _signal;

	uint8_t _multi_thread_num;
	uint8_t _current_thread_index;
	std::vector<std::unique_ptr<boost::asio::io_service> > _connection_io_service_pool;

	root_router* _root_router;
};
#endif // http_server_h__
